<?php
/**
 * DolphinGTK - phpGtk framework
 *
 * @package    DolphinGTK
 * @author     Askarov Ruslan
 * @copyright  Askarov Ruslan (c) 2010.
 * @link       http://groups.google.com/group/dolphingtk
 * @license    http://www.opensource.org/licenses/gpl-license.php GPL
 * @version    0.1
 * @since      PHP v.: 5.2.1
 * @subpackage desktop
 * @category   library
 * @filesource
 */

final class Table
{
  ///////////////////////////////////////
  // эти переменные остаются доступными.
  //чтобы можно было воспользоваться,
  // при надобности
  public  $store;
  public  $tree;
  ///////////////////////////////////////
  private $reserve;
  private $sortable;
  private $sortable_colums;
  private $editable;
  private $editable_foo;
  private $editable_fields;
  private $num_field;
  private $type;
  private $row_color;
  private $lines;


  public function reserve( $nums = 0 )
  {
    if( is_numeric($nums) && $nums > 0 )
    {
      $this->reserve = $nums;
    }
  }




  public function sortable( $flag = false, $colums = false )
  {
    if( is_bool($flag) )
    {
      $this->sortable = $flag;
    }

    if( is_array($colums) )
    {
      $this->sortable_colums = $colums;
    }
  }




  public function editable( $flag = false, $editable_foo = false, $fields = false )
  {
    if( is_bool($flag) )
    {
      $this->editable = $flag;
    }

    if( is_array($editable_foo) && count($editable_foo) == 2 )
    {
      if( class_exists( $editable_foo[0]) )
      {
        $this->editable_foo = $editable_foo;
      }
    }

    if( is_array($fields) ) {
      $this->editable_fields = $fields;
    }

  }




  public function lines( $type )
  {
    switch($type)
    {
      case 'horizontal':
        $type = Gtk::TREE_VIEW_GRID_LINES_HORIZONTAL;
        break;
      case 'vertical':
        $type = Gtk::TREE_VIEW_GRID_LINES_VERTICAL;
        break;
      case 'both':
        $type = Gtk::TREE_VIEW_GRID_LINES_BOTH;
        break;
      default:
        $type = Gtk::TREE_VIEW_GRID_LINES_NONE;
        break;
    }

    $this->lines = $type;
  }



  
  public function on_key( $callback )
  {
    if( is_array($callback) && count($callback) == 2 ) {
      $this->tree->connect(
        'key-press-event', array($this, '_on_key'),
        $this->store, $callback
      );
    }
  }




  public function on_bottom($callback)
  {
    if( is_array($callback) && count($callback) == 2 ) {
      $this->tree->connect(
        'button-press-event', array('Table', '_on_button'),
        $this->store, $callback
      );
    }
  }


  public static function _on_key($widget, $event, $store, $callback)
  {
    $keyval = $event->keyval;
    $row = $widget->get_cursor();
    call_user_func_array( $callback, array($keyval, $row[0][0], $event) );
  }




  public static function _on_button($view, $event, $store, $callback)
  {
    $path_array = $view->get_path_at_pos($event->x, $event->y);
    $row = $path_array[0][0];
    call_user_func_array( $callback, array($event->button, $row, $event) );
  }




  public function create()
  {
    $arg = func_get_args();

    $fields = array(
      'checkbox' => 'GObject::TYPE_BOOLEAN',
      'icon'     => 'GdkPixbuf::gtype',
      'progress' => 'GObject::TYPE_LONG',
      'radio'    => 'GObject::TYPE_BOOLEAN',
      'select'   => 'GObject::TYPE_STRING'
    );

    $types = array(
      'string'  => 'GObject::TYPE_STRING',
      'decimal' => 'GObject::TYPE_DOUBLE',
      'int'     => 'GObject::TYPE_LONG',
      'bool'    => 'GObject::TYPE_BOOLEAN'
    );

    if( count($arg) == 0 ) {
      print "Enter table header in 'create' function\n";
      return false;
    }

    foreach( $arg as $i => $values )
    {
      if( is_array($values) && count($values) >= 2 )
      {
        $type  = $values[0];
        $title = $values[1];
        
        if( key_exists($type, $types) )
        {
          $this->type[] = $type;
          $set[$i] = $types[$type];
          $titles[$i] = $title;
        }
        elseif( key_exists($type, $fields) )
        {
          $this->type[] = $type;
          $set[$i] = $fields[$type];
          $titles[$i] = $title;
        }
      }
      else
      {
        print "В значении заголовка, должно быть два поля.\n";
        return false;
      }
    }


    eval('$this->store = new GtkListStore('.implode(', ',$set).');');

    if( $this->store )
    {
      $this->tree = new GtkTreeView($this->store);
      $this->tree->set_enable_search(true);

      if( $this->lines ) {
        $this->tree->set_grid_lines($this->lines);
      }

      foreach( $titles as $i => $title )
      {
        $this->num_field ++;
        if( $arg[$i][0] == 'checkbox' )
        {
          $render_toggle = new GtkCellRendererToggle();
          $column = new GtkTreeViewColumn($title, $render_toggle, 'active', $i);

          if( $this->row_color ) {
            $column->set_cell_data_func($render_toggle, array($this, 'format_col'), $i);
          }

          $render_toggle->connect('toggled', array($this, 'on_checked'), $i, $this);
        }
        elseif( $arg[$i][0] == 'radio' )
        {
          $render_toggle = new GtkCellRendererToggle();
          $render_toggle->set_property('activatable', true);
          $render_toggle->set_radio(true);
          $column = new GtkTreeViewColumn($title, $render_toggle, 'active', $i);
          $column->set_attributes($render_toggle, 'active', $i);

          if( $this->row_color ) {
            $column->set_cell_data_func($render_toggle, array($this, 'format_col'), $i);
          }
          
          $render_toggle->connect('toggled', array('Table', 'on_checked'), $i, $this);
        }
        elseif( $arg[$i][0] == 'icon' )
        {
          $render_toggle = new GtkCellRendererPixbuf();
          $column = new GtkTreeViewColumn($title, $render_toggle, 'pixbuf', $i);

          if( $this->row_color ) {
            $column->set_cell_data_func($render_toggle, array($this, 'format_col'), $i);
          }
        }
        elseif( $arg[$i][0] == 'progress' )
        {
          $render_toggle = new GtkCellRendererProgress();
          $column = new GtkTreeViewColumn($title, $render_toggle, 'value', $i);

          if( $this->row_color ) {
            $column->set_cell_data_func($render_toggle, array($this, 'format_col'), $i);
          }
        }
        elseif( $arg[$i][0] == 'select' )
        {
          if( is_array($arg[$i][2]) )
          {
            $category = new GtkListStore(GObject::TYPE_STRING);
            $list = $arg[$i][2];

            foreach( $list as $choice ) {
              $category->append(array($choice));
            }

            $render_toggle = new GtkCellRendererCombo();
            $render_toggle->set_property('model', $category);
            $render_toggle->set_property('text-column', 0);
            $render_toggle->set_property('editable', true);
            $render_toggle->set_property('has-entry', false);

            $column = new GtkTreeViewColumn($title, $render_toggle, 'text', $i);

            if( $this->row_color ) {
              $column->set_cell_data_func($render_toggle, array($this, 'format_col'), $i);
            }
          }
          else
          {
            print "Введите список\n";
          }
        }
        else
        {
          $render = new GtkCellRendererText;
          $render->set_property('ellipsize', Pango::ELLIPSIZE_END);
          $column = new GtkTreeViewColumn($title, $render, 'text', $i);

          if( $this->row_color ) {
            $column->set_cell_data_func($render, array($this, 'format_col'), $i);
          }
          
          $column->set_expand(true);
          $column->set_resizable(true);

          if( $this->editable === true )
          {
            if( is_array($this->editable_fields) && in_array($i, $this->editable_fields))
            {
              $render->set_property('editable', true);
              $render->connect("edited", array($this, 'on_edit_done'), $this, $i);
            }
            elseif( !is_array($this->editable_fields) )
            {
              $render->set_property('editable', true);
              $render->connect("edited", array($this, 'on_edit_done'), $this, $i);
            }
          }

          if( $this->sortable === true )
          {
            if( is_array($this->sortable_colums) && in_array($i, $this->sortable_colums))
            {
              $column->set_sort_column_id($i);
            }
            elseif( !is_array($this->sortable_colums) )
            {
              $column->set_sort_column_id($i);
            }
          }
        }
        
        $this->tree->append_column($column);
      }


      // если зарезервировали
      if( is_numeric($this->reserve) ):
      $n = $this->reserve;
      for( $i = 0; $i < $n; $i ++) {
        $null = str_repeat("','", ($this->num_field-1));
        eval('$this->store->append(array(\''.$null.'\'));');
      }
      endif;
    }
    else
    {
      print "Что-то пошло не так...\n";
    }
  }




  public function on_edit_done( $cell, $path, $new_value, $view, $col )
  {
    if( $view->editable === true ):

    $modelsort = $view->tree->get_model();
    $iter = $modelsort->get_iter_from_string($path);

    $old_value = $modelsort->get_value($iter, $col);

    // проверим обновлены ли данные
    if( $new_value != $old_value ):
    
    // если да, то внесем их
    $modelsort->set($iter, $col, $new_value);

    // считаем весь ряд
    for( $i = 0; $i < $view->num_field; $i ++ )
    {
      $arg[] = $modelsort->get_value($iter, $i);
    }

    $row = $view->tree->get_cursor();
    
    if( is_array($row) ) {
      $row = $row[0][0];
      $arg[] = $row;
    }
    
    $arg[] = $old_value;

    // и вернем
    call_user_func_array( $view->editable_foo, $arg );

    endif;

    endif;
  }


  public function on_checked( $renderer, $row, $i, $table )
  {
    if( $table->editable === true ):

    $iter  = $table->store->get_iter($row);
    $value = $table->store->get_value($iter, $i);
    $table->store->set($iter, $i, !$value );

    for( $i = 0; $i < $table->num_field; $i ++ )
    {
      $arg[] = $table->store->get_value($iter, $i);
    }
    
    $arg[] = $row;
    
    call_user_func_array( $table->editable_foo, $arg );
    
    endif;
  }




  public function get($height = false)
  {
    if( !$height ) return $this->tree;
    
    $scrool = new GtkScrolledWindow();
    $scrool->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
    $scrool->set_size_request(0, $height);

    if( $this->tree ) {
      $scrool->add($this->tree);
    } else {
      print "Table is not created!\n";
    }

    return $scrool;
  }




  public function delete( $row = false )
  {
    if( !is_numeric($row) ) {
      $row = $this->tree->get_cursor();
      if( is_array($row) ) {
        $row = $row[0][0];
      }
    }

    if( is_numeric($row) ) {
      $iter  = $this->store->get_iter((int)$row);
      $this->store->remove( $iter );
    }
  }




  public function update( $row, $col = false, $text = false )
  {
    if( is_numeric( $row ) )
    {
      $iter  = $this->store->get_iter($row);
      $modelsort = $this->tree->get_model();

      if( is_numeric($col) && !is_array($text) )
      {
        $modelsort->set($iter, $col, $text);
      }
      elseif( is_array($text) )
      {
        for( $i = 0; $i < $this->num_field; $i ++ )
        {
          if( $this->type[$i] == 'checkbox' ) {
            $text[$i] = (!$text[$i]) ? false : true;
          }
          $modelsort->set($iter, $i, $text[$i]);
        }
      }
    }
    else
    {
      print "Row is not numeric!";
    }
  }



  public function format_col($column, $cell, $model, $iter, $col_num)
  {
    $path = $model->get_path($iter);
    $row_num = $path[0];
    $row_color = ($row_num%2==1) ? $this->row_color[0] : $this->row_color[1];
    $cell->set_property('cell-background', $row_color);
  }



  public function insert( $data, $last_row = false )
  {
    if( is_array($data) && count($data) == $this->num_field) {
      if( $this->reserve > 0 && $last_row ) {
        for( $i = 0; $i < $this->reserve; $i ++)
        {
          $row = self::get_values($i);
          $row = array_unique($row);
          if( count($row) == 1 ) {
            $this->update($i, false, $data);
            $inserted = true;
            break;
          }
        }
        if( !isset($inserted) ) {
          $this->store->append($data);
        }
      }
      else {
        $search_icon = array_search('icon', $this->type);

        if( is_numeric($search_icon) ) {
          $data[$search_icon] = $this->tree->render_icon(
            get_icon($data[$search_icon]),
            Gtk::ICON_SIZE_SMALL_TOOLBAR
          );
        }
        
        $this->store->append($data);
      }
    }
  }



  public function get_values( $row = false, $col = false)
  {
    if( is_numeric( $row ) )
    {
      $iter  = $this->store->get_iter($row);
      $modelsort = $this->tree->get_model();

      if( is_numeric( $col ) )
      {
        return $modelsort->get_value($iter, $col);
      }

      for( $i = 0; $i < $this->num_field; $i ++ )
      {
        $arg[] = $modelsort->get_value($iter, $i);
      }

      return $arg;
    }
  }


  public function row_color($a, $b)
  {
    if( !empty($a) && !empty($b) ) {
      $this->row_color = array($a, $b);
    }
  }


  public function menu($menu)
  {
    if( is_array($menu) && count($menu) > 0)
    {
      $menu = Menu::create($menu);
      $menu->show_all();
      $menu->popup();
    }
  }




  public function clear()
  {
    $this->store->clear();
  }




  public function focus( $row = 0 )
  {
    $this->tree->set_cursor((int)$row);
  }
}
?>
